﻿using System;
using System.Collections.Generic;
using System.Threading;
namespace Multithread
{
    class QueueProcessor
    {
        public static int NumberofThreads { private set; get; }
        public static int MaxThreadCount { get; set; }
        private static Queue<string> TheQueue = new Queue<string>();
        public Thread TheThread { get;private set;}
        public string ThreadName { get; private set; }
        public static int QueueCount { get { return TheQueue.Count; } private set { } }

        public static void Enqueue(string str){
            lock (TheQueue) {
                TheQueue.Enqueue(str);
            }
        }
        public static string DeQueue(){
            lock (TheQueue)
            {
                return TheQueue.Dequeue();
            }
        }
        
        
        public QueueProcessor(string threadName)
        {
            TheThread = new Thread(new ThreadStart(this.ThreadFunc));
            ThreadName = threadName; 
        }

      
        public void BeginProcessData() {
            TheThread.Start();
            NumberofThreads++;
            //Console.WriteLine(string.Format("--Started thread {0}", ThreadName));
        }

        public void EndProcessData() {
            TheThread.Join();
            NumberofThreads--;
            //Console.WriteLine(string.Format("--Ended thread {0}", ThreadName));
        }

        private static void CheckMaxConcurrentThreads()
        {
            if (MaxThreadCount < NumberofThreads) MaxThreadCount = NumberofThreads;
        }

        private void ThreadFunc()
        {

            string s = (TheQueue.Count > 0) ? TheQueue.Dequeue() : "";
            var random = new Random();
            while (!string.IsNullOrEmpty(s))
            {
                //Console.WriteLine(string.Format("Thread {0} (id: {1}) processing '{2}'; Thread Count: {3}", ThreadName, TheThread.ManagedThreadId, s, NumberofThreads));
                Console.Write("\r{0}       ", TheQueue.Count);
                DoCalculation();
                s = (TheQueue.Count > 0) ? TheQueue.Dequeue() : ""; ;
                CheckMaxConcurrentThreads();
            }

        }


        /// <summary>
        /// i am creating this method to test the QueuUserThread method
        /// </summary>
        /// <param name="stateInfo"></param>
        public static void WorkMethod(object stateInfo)
        {
            var state = (ThreadInfo) stateInfo;
            /*Console.WriteLine("ThreadTwo, executing WorkMethod, " +
                "is {0}from the thread pool.",
                Thread.CurrentThread.IsThreadPoolThread ? "" : "not ");*/

            
            string s = (TheQueue.Count > 0) ? TheQueue.Dequeue() : "";
            while (!string.IsNullOrEmpty(s))
            {
                //Console.WriteLine(string.Format("    Thread {0} (id: {1}) processing '{2}'", state.ThreadName, System.Threading.Thread.CurrentThread.ManagedThreadId, s));
                Console.Write("\r{0}       ", TheQueue.Count);
                DoCalculation();
                if (state.ThreadNumber > MaxThreadCount) MaxThreadCount = state.ThreadNumber;
                s = (TheQueue.Count > 0) ? TheQueue.Dequeue() : ""; ;
            }
            
            // Signal that this thread is finished.
            state.AutoEvent.Set();
            
        }

        public static int DoCalculation()
        {
            int factorial=1, number = 1;
            while (number > 0 && number <= 20)
            {
                factorial *= number;
                number++;
            } 
            System.Threading.Thread.Sleep(1);
            return factorial;
        }


    }

    internal class ThreadInfo
    {
        public string ThreadName { get; set; }
        public AutoResetEvent AutoEvent { get; set; }
        public int ThreadNumber;

        public ThreadInfo(string threadName, AutoResetEvent autoEvent, int threadNumber)
        {
            ThreadName = threadName;
            AutoEvent = autoEvent;
            ThreadNumber = threadNumber;


        }
    }
}
